package com.github.cdisk.xtrans.ui;

import java.lang.reflect.Field;
import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.wb.swt.SWTResourceManager;

import com.github.cdisk.xtrans.annotiation.Component;
import com.github.cdisk.xtrans.annotiation.ConnectLine;
import com.github.cdisk.xtrans.core.BaseComponent;
import com.github.cdisk.xtrans.core.BaseComponentUI;
import com.github.cdisk.xtrans.ui.event.ComponentMouseListener;
import com.github.cdisk.xtrans.ui.toolbox.AttributeBoxLoader;
import com.github.cdisk.xtrans.ui.toolbox.ComponentBoxLoader;
import com.github.cdisk.xtrans.ui.util.ComponentReflect;

/**
 * 中间主要画板
 * @author Cdisk
 *
 */
public class CanvasPanel extends Canvas implements PaintListener, MouseListener, MouseMoveListener, MenuDetectListener, KeyListener {

	/** 被鼠标点中的元件 */
	private BaseComponent mouseDownComp;

	/** 鼠标点中元件时，被点中元件和鼠标事件点的差值 ，拖动元件时使用。*/
	private Point eventOffsetPoint = new Point(0, 0);
	
	/** 鼠标点击元件后，加载属性面板 */
	private AttributeBoxLoader attributeLoad;

	/** 从左侧面板点击的，准备添加的元件 */
	private BaseComponent readyComponent;

	/** 全局组件上下文 */
	private Context context;


	public CanvasPanel(Composite parent, int style, Context context) {
		super(parent, style);
		this.context = context;
		addPaintListener(this);
		addMouseListener(this);
		addMouseMoveListener(this);
		addMenuDetectListener(this);
		addKeyListener(this);
	}

	@Override
	public void paintControl(PaintEvent e) {
		GC gc = e.gc;
		gc.setAntialias(SWT.ON);
		gc.setLineWidth(1);

		List<BaseComponent> components = Operation.CONFIG.components;
		Point p = null;
		Point tagP = null;
		Point maxSize = new Point(0, 0); // 元件需要的最大宽度和最大高度

		/* 先画线,  first draw line. */
		for (BaseComponent component : components) {
			
			p = new Point(component.getUi().getX(), component.getUi().getY());
			if (p.x > maxSize.x) maxSize.x = p.x;
			if (p.y > maxSize.y) maxSize.y = p.y;

			Field[] fields;
			Class<?> classType = component.getClass();
			while (!Object.class.equals(classType) && classType != null) {
				fields = classType.getDeclaredFields();// 获取当前类所有的属性
				for (Field f : fields) {
					ConnectLine line = f.getAnnotation(ConnectLine.class);
					if (line != null) {
						f.setAccessible(true);
						try {
							tagP = getTagPorint((BaseComponent) f.get(component));
						} catch (IllegalArgumentException e1) {
							e1.printStackTrace();
						} catch (IllegalAccessException e1) {
							e1.printStackTrace();
						}
					}
					if (tagP != null) {
						gc.setForeground(SWTResourceManager.getColor(line.color()));
						gc.setBackground(SWTResourceManager.getColor(line.color()));
						gc.drawLine(p.x, p.y, tagP.x, tagP.y);
						paintk(gc, p.x, p.y, tagP.x, tagP.y);
					}
					tagP = null;
				}
				classType = classType.getSuperclass();// 向上遍历父类
			}
		}

		/* 再画图标, draw icon after draw line. */
		for (BaseComponent component : components) {
			draw(gc, component);
		}
		
		/* 如果有准备放入的元件，就绘制 */
		if (readyComponent != null) {
			gc.setAlpha(128);
			draw(gc, readyComponent);
			gc.setAlpha(255);
		}

		/* 处理滚动条 */
		ScrolledComposite composite = (ScrolledComposite) this.getParent().getParent();
		ScrollBar verticalBar = composite.getVerticalBar();
		ScrollBar horizontalBar = composite.getHorizontalBar();
		maxSize.x = Math.max(maxSize.x + ComponentUI.SIZE.x * 2, composite.getSize().x - composite.getBorderWidth() * 2 - verticalBar.getSize().x); // 默认和外框一样大
		maxSize.y = Math.max(maxSize.y + ComponentUI.SIZE.y * 2, composite.getSize().y - composite.getBorderWidth() * 2 - horizontalBar.getSize().y); // 默认和外框一样大
		this.setBounds(0, 0, maxSize.x, maxSize.y); // 设置当前Canvas大小
		composite.setMinSize(this.getParent().computeSize(SWT.DEFAULT, SWT.DEFAULT)); // 设置外部滚动条组件滚动大小
	}

	private Point getTagPorint(BaseComponent component) {
		List<BaseComponent> components = Operation.CONFIG.components;
		for (BaseComponent comp : components) {
			if (component == comp) {
				Point p = new Point(comp.getUi().getX(), comp.getUi().getY());
				return p;
			}
		}
		return null;
	}

	/**
	 * 画中间带箭头的连接线
	 * 
	 * @param g
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 */
	public void paintk(GC g, int x1, int y1, int x2, int y2) {
		int lineX = x2;
		int lineY = y2;
		x2 = (x1 + x2) / 2;
		y2 = (y1 + y2) / 2;
		// 画线
		g.drawLine(x1, y1, lineX, lineY);

		// 三角形 点1
		double r = 0;
		double xOffset = x2 - x1;
		double yOffset = y2 - y1;
		r = Math.atan2(yOffset, xOffset);

		int[] t1 = rotate(15, 0, r);
		int[] t2 = rotate(0, -7, r);
		int[] t3 = rotate(0, 7, r);

		t1[0] = t1[0] + x2;
		t1[1] = t1[1] + y2;
		t2[0] = t2[0] + x2;
		t2[1] = t2[1] + y2;
		t3[0] = t3[0] + x2;
		t3[1] = t3[1] + y2;

		g.fillPolygon(new int[] { t1[0], t1[1], t2[0], t2[1], t3[0], t3[1] });
	}

	/**
	 * 计算旋转角度后的新位置
	 * @param x
	 * @param y
	 * @param r
	 * @return
	 */
	private int[] rotate(int x, int y, double r) {
		double sr = Math.sin(r);
		double cr = Math.cos(r);
		int x0 = (int) (x * cr - y * sr);
		int y0 = (int) (x * sr + y * cr);
		return new int[] { x0, y0 };
	}

	@Override
	public void mouseDoubleClick(MouseEvent event) {
		List<BaseComponent> components = Operation.CONFIG.components;
		for (BaseComponent comp : components) {
			ComponentUI compUI = ((ComponentUI) comp.getUi());
			if (compUI.mouseEvent(event)) {
				ComponentMouseListener listener = compUI.getListener();
				if (listener != null) {
					listener.mouseDoubleClick(event, comp);
				}
			}
		}
	}

	/**
	 * 当鼠标在画布中点击时的事件。
	 * @param event
	 */
	@Override
	public void mouseDown(MouseEvent event) {
		if (readyComponent != null) { // 如果点击鼠标时，有准备添加的元件，则添加新的元件
			System.out.println(event.button);
			if (event.button == 1) { // 左键时，添加元件
				Operation.CONFIG.components.add(readyComponent);
				context.getListObjects().reflushItems();
				readyComponent = null;
			} else if (event.button == 3) { // 右键时，取消添加
				readyComponent = null;
			}
		} else {
			if (attributeLoad != null) attributeLoad.load(null); // 默认无元件被选中，所以应该清空属性面板内容。
			/* 找到被点中的元件。 */
			List<BaseComponent> components = Operation.CONFIG.components;
			for (BaseComponent comp : components) {
				ComponentUI compUI = ((ComponentUI) comp.getUi());
				compUI.setSelection(false);
				if (compUI.mouseEvent(event)) { // 判断是否点中元件
					mouseDownComp = comp; // 用这种外部变量的方法，是为了避免重叠元素点击时全部被选中，这样可以只选中最顶层一个元素。
				}
			}
			/* 有选中元件，处理选中逻辑。 */
			if (mouseDownComp != null) {
				ComponentUI compUI = ((ComponentUI) mouseDownComp.getUi());
				compUI.setSelection(true);
				eventOffsetPoint.x = mouseDownComp.getUi().getX() - event.x;
				eventOffsetPoint.y = mouseDownComp.getUi().getY() - event.y;
				ComponentMouseListener listener = compUI.getListener();
				if (listener != null) {
					listener.mouseDown(event, mouseDownComp);
				}
				if (attributeLoad != null) attributeLoad.load(mouseDownComp); // 触发属性面板加载元件属性
			}

		}
		this.redraw();
	}

	@Override
	public void mouseUp(MouseEvent event) {
		mouseDownComp = null;
		List<BaseComponent> components = Operation.CONFIG.components;
		for (BaseComponent comp : components) {
			ComponentUI compUI = ((ComponentUI) comp.getUi());
			if (compUI.mouseEvent(event)) {
				ComponentMouseListener listener = compUI.getListener();
				if (listener != null) {
					listener.mouseUp(event, comp);
				}
			}
		}
	}

	/**
	 * 鼠标在画布中的移动事件，主要是处理元件拖动事件。
	 */
	@Override
	public void mouseMove(MouseEvent event) {
		if (readyComponent != null) { // 如果有准备放入的元件，那么现在鼠标移动时，应该是绘制准备放入的元件。
			readyComponent.getUi().setX(event.x);
			readyComponent.getUi().setY(event.y);
		} else {
			if (mouseDownComp != null) {
				int tagX = Math.max(event.x + eventOffsetPoint.x, ComponentUI.SIZE.x);
				int tagY = Math.max(event.y + eventOffsetPoint.y, ComponentUI.SIZE.y);
				mouseDownComp.getUi().setX(tagX);
				mouseDownComp.getUi().setY(tagY);
			}
			List<BaseComponent> components = Operation.CONFIG.components;
			for (BaseComponent comp : components) {
				ComponentUI compUI = ((ComponentUI) comp.getUi());
				if (compUI.mouseEvent(event)) {
					ComponentMouseListener listener = compUI.getListener();
					if (listener != null) {
						listener.mouseMove(event, comp);
					}
				}
			}
		}
		this.redraw();
	}

	/**
	 * 点击右键，菜单弹出来
	 * @param e
	 */
	@Override
	public void menuDetected(MenuDetectEvent e) {
		/* 如果鼠标上有准备添加的元件，右键时仅代表取消添加操作。 */
		if (readyComponent != null) {
			return;
		}
		
		/* 找出点中的元件对象 */
		BaseComponent component = null;
		for (BaseComponent comp : Operation.CONFIG.components) {
			ComponentUI compUI = ((ComponentUI) comp.getUi());
			if (compUI.isSelection()) {
				component = comp;
				break;
			}
		}
		if (component != null) {
			final BaseComponent finalComponent = component;
			Menu menu = new Menu(this);
			menu.setVisible(true);
			MenuItem menuItem = new MenuItem(menu, SWT.NONE);
			menuItem.setText("删除");
			menuItem.addSelectionListener(new SelectionListener() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					Operation.CONFIG.components.remove(finalComponent); // 从元件列表中删除
					for (BaseComponent comp : Operation.CONFIG.components) {
						ComponentReflect.RemoveComponentQuote(comp, finalComponent); // 删除所有的元件引用
					}
				}
				@Override
				public void widgetDefaultSelected(SelectionEvent e) {}
			});
		}
	}

	@Override
	public void keyPressed(KeyEvent e) {
		if (e.keyCode == 127) { // 删除按钮
			/* 找出点中的元件对象 */
			BaseComponent component = null;
			for (BaseComponent comp : Operation.CONFIG.components) {
				ComponentUI compUI = ((ComponentUI) comp.getUi());
				if (compUI.isSelection()) {
					component = comp;
					break;
				}
			}
			if (component != null) {
				Operation.CONFIG.components.remove(component); // 从元件列表中删除
				for (BaseComponent comp : Operation.CONFIG.components) {
					ComponentReflect.RemoveComponentQuote(comp, component); // 删除所有的元件引用
				}
			}
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
	}

	/**
	 * 绘制元件方法
	 * @param gc 绘制对象
	 * @param component 要绘制的元件
	 */
	private void draw(GC gc, BaseComponent component) {
		int width = ComponentUI.SIZE.x; // 绘制大小-宽度
		int height = ComponentUI.SIZE.y; // 绘制大小-高度
		BaseComponentUI ui = component.getUi();
		
		/* 居中画出图标 */
		Component compa = component.getClass().getAnnotation(Component.class);
		Image image = SWTResourceManager.getImage(ComponentBoxLoader.class.getResource("/").getPath() + "/icon/" + compa.icon());
		gc.drawImage(image, 0, 0, image.getBounds().width, image.getBounds().height, ui.getX() - width / 2, ui.getY() - height / 2, width, height);

		/* 绘制选中效果 */
		if (ui.isSelection()) {
			gc.setForeground(SWTResourceManager.getColor(SWT.COLOR_BLUE));
			gc.setLineStyle(SWT.LINE_DASH);
			gc.drawRectangle(ui.getX() - width / 2 - 1, ui.getY() - height / 2 - 1, width + 1, height + 1);
		}

		/* 居中绘制ID文字 */
		Point size = gc.stringExtent(component.getId());
		gc.setForeground(SWTResourceManager.getColor(SWT.COLOR_BLACK));
		gc.drawString(component.getId(), ui.getX() - size.x / 2, ui.getY() + height / 2, true);
	}

	/**
	 * 设置点击元件后加载属性面板事件
	 * @param attributeLoad
	 */
	public void setOnAttributeLoad(AttributeBoxLoader attributeLoad) {
		this.attributeLoad = attributeLoad;
	}

	/**
	 * 设置一个准备添加的元件，使其可以跟随鼠标
	 * @param readyComponent
	 */
	public void setReadyComponent(BaseComponent readyComponent) {
		this.readyComponent = readyComponent;
	}

}
